#include "socketutils.h"

void setSocketAddress(SocketAddress *address, sa_family_t family, int addr, int port) {
    address->sin_family = family;
    address->sin_port = htons(port);
    address->sin_addr.s_addr = htonl(addr);
}

bool setSocketConnectAddress(SocketAddress *address, sa_family_t family, const char *addr, int port) {
    address->sin_family = family;
    address->sin_port = htons(port);

    int s = inet_pton(AF_INET, addr, &address->sin_addr);
    if (s < 0) return false;
    return true;
}

// ------------------------------------------------------

/* 设置
 * extern int setsockopt (int __fd, 
                     int __level, 
                     int __optname,
                         const void *__optval, 
                     socklen_t __optlen) __THROW;
 * __fd: 指向一个打开的套接描述字
 * __level: 指定选项代码的类型
 * 		SOL_SOCKET: 基本套接口
 *		IPPROTO_IP:   IPv4套接口
 *		IPPROTO_IPV6: IPv6套接口
 *		IPPROTO_TCP:  TCP套接口
 * __optname: 选项名称
 * 		SOL_SOCKET: 
         SO_BROADCASE	允许套接口传送广播信息
         SO_DEBUG		记录调试信息
         SO_DONTROUTE	禁止选径；直接传送
         SO_ERROR		获取错误状态并清除
         SO_KEEPPALIVE	发送“保持活动”包
         SO_DONTLINGER	不要因为数据未发送就阻塞关闭操作，设置本选项相当于SO_LINGER的 I_onoff元素置为零
         SO_LINGER		如关闭时有未发送数据，则逗留。
         SO_REUSEADDR	允许套接口和一个已在使用的地址捆绑

 * __optval: 选项值，是一个指向变量的指针。类型：整型，套接口结构，其它结构类型： linger{}, timeval{}
 * __optlen: 选项长度，optval的大小(通常使用sizeof())
 */
// (int socket, int level, int option_name, const void *option_value, socklen_t option_len)
bool socketSetOption(TcpSocket *tcp, int level, int option_name, const void *option_value, socklen_t option_len) {
    int s = setsockopt(tcp->socket, level, option_name, option_value, option_len);
    if (s < 0) return false;
    return true;
}

/*绑定套接字端口*/
// bind(int socket, const struct sockaddr *address, socklen_t address_len);
bool socketBind(TcpSocket *tcp, const struct sockaddr *address, socklen_t address_len) {
    int s = bind(tcp->socket, address, address_len);
    if (s < 0) return false;
    return true;
}

/*监听客户端请求*/
// listen(int socket, int backlog)
bool socketListen(TcpSocket *tcp, int backlog) {
    int s = listen(tcp->socket, backlog);
    if (s < 0) return false;
    return true;
}

/*接收客户请求*/
// int accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len);
bool socketAccept(TcpSocket *tcp, TcpSocket *client) {
    // SocketAddress clientAddress = {0};
    socklen_t address_len = sizeof(client->address);
    client->socket = accept(tcp->socket, (struct sockaddr *)&client->address, &address_len);
    if (client->socket < 0) return false;
    return true;
}

/*发送链接服务器请求*/
// connect(int socket, const struct sockaddr *address, socklen_t address_len);
bool socketConnect(TcpSocket *tcp, const SocketAddress *address, socklen_t address_len) {
    int s = connect(tcp->socket, (struct sockaddr *)address, address_len);
    if (s < 0) return false;
    return true;
}

// ------------------------------------------------------

// socket(domain, type, protocol)
    // domain: 
        // AF_INET(IPv4 protocol)
        // AF_INET6(IPv6 protocol)
    // type:
        // SOCK_STREAM: TCP
        // SOCK_DGRAM: UDP
    // protocol:
        // Internet 协议 (IP) 的协议值，为 0。这与出现在数据包 IP 标头中的协议字段上的数字相同。（man 协议了解更多详情）
bool createTcpSocket(TcpSocket *tcp) {
    tcp->socket = socket(AF_INET, SOCK_STREAM, 0);
    if (tcp->socket < 0) return false;
    return true;
}

bool createTcpSocketServer(TcpSocketServer *tcp, int port) {
    SocketAddress serverAddress = {0};
    setSocketAddress(&serverAddress, AF_INET, INADDR_ANY, port);

    int option = SO_REUSEADDR;
    int backlog = 5;
    if (createTcpSocket(tcp)) {
        socketSetOption(tcp, SOL_SOCKET, option, &option, sizeof(option));
        if (socketBind(tcp, (struct sockaddr *)&serverAddress, sizeof(serverAddress))) {
            return socketListen(tcp, backlog);
        }
    }
    return false;
}

bool createTcpSocketWithHostPort(TcpSocket *tcp, const char *host, int port) {
    // SocketAddress clientAddress = {0};
    setSocketConnectAddress(&tcp->address, AF_INET, host, port);

    if (createTcpSocket(tcp)) {
        // return socketConnect(socket, (struct sockaddr *)&clientAddress, sizeof(clientAddress));
        return true;
    }
    return false;
}
//
// bool createTcpClientSocket(int *socket, int *client) {
    // return socketAccept(socket, client);
// }

// ------------------------------------------------------
ssize_t socketRead(int socket, char *buffer, size_t len) {
    // flags:
    // MSG_OOB        process out-of-band data
    // MSG_PEEK       peek at incoming message
    // MSG_WAITALL    wait for full request or error

    // ssize_t recv(int socket, void *buffer, size_t length, int flags);
    // ssize_t recvfrom(int socket, void *restrict buffer, size_t length, int flags, struct sockaddr *restrict address, socklen_t *restrict address_len);
    // ssize_t recvmsg(int socket, struct msghdr *message, int flags);

    return recv(socket, buffer, len, 0);
}

ssize_t socketWrite(int socket, char *buffer, size_t len) {
    // flags:
    // #define MSG_OOB        0x1  /* process out-of-band data */
    // #define MSG_DONTROUTE  0x4  /* bypass routing, use direct interface */

    // ssize_t send(int socket, const void *buffer, size_t length, int flags);
    // ssize_t sendmsg(int socket, const struct msghdr *message, int flags);
    // ssize_t sendto(int socket, const void *buffer, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len);

    return send(socket, buffer, len, 0);
}

// ------------------------------------------------------
ssize_t tcpSocketRead(TcpSocket *tcp, char *buffer, size_t len) {
    return socketRead(tcp->socket, buffer, len);
}
ssize_t tcpSocketWrite(TcpSocket *tcp, char *buffer, size_t len) {
    return socketWrite(tcp->socket, buffer, len);
}